// 组件在mount和update时，调用的是不一样的 所以要区分一下
var isMount = true;
// 标记一下我们当前正在处理那个useState
var workInProgressHook = null;

// 每一个组件不论是函数组件、类组件还是原生的dom 都有一个对应的fiber 这个fiber指向当前的组件
const fiber = {
  stateNode: App,
  // memoizedState 在函数组件中用于保存 useState的值，在class组件中用于保存定义的state = {}
  // 在函数组件中，useState可以被调用很多次，那么memoizedState如何保存这些值？
  // 答：memoizedState采用链表的形式，以此从上至下去获取保存useState，这就是为什么不允许 useState在if中调用的原因
  memoizedState: null,
};

// 调度 使我们的mini useState能运行起来
function schedule() {
  // 每次重新调度的时候 让workInProgressHook复位指向第一个
  workInProgressHook = fiber.memoizedState;
  // 调用触发 返回组件（这里返回的是一个对象）
  const app = fiber.stateNode();
  // 运行后 将表示改编为已经mount了
  isMount = false;
  return app;
}

function useState(initialState) {
  // 1. 我们可以调用很多次 useState，那我如何获取到我们当前调用的hook对象？
  let hook;
  // 如果是首次渲染
  if (isMount) {
    // 创建一个hook
    // hook也有一个memoizedState 保存hook的值
    // 因为fiber.memoizedState是一个链表 （链表：当前属性的next属性指向下一个属性的地址）
    hook = {
      memoizedState: initialState,
      next: null,
      // hook的状态，当我们调用 setX 来改变hook x的值是，我们要监听到她的状态是否改变完成
      queue: {
        pending: null,
      },
    };

    // 初始化创建好hook后， 把hook绑定到 fiber.memoizedState 上
    // 为什么要判断fiber.memoizedState 因为我们会调用多次useState,
    // mount下
    // useState(1)  第一次调用 fiber.memoizedState 为 null 所以创建一个hook
    // useState(2)  第二次调用 fiber.memoizedState 为 hook1 所以要把hook绑定到hook1的next下，而不是重新赋值

    if (!fiber.memoizedState) {
      // 保存到fiber中
      fiber.memoizedState = hook;
      // 注：workInProgressHook 只保存地址，其实等同于hook 等同于 fiber.memoizedState
      // workInProgressHook改变 hook 和 fiber.memoizedState 也会改变
    } else {
      // 将下一个hook和上一个hook连接起来
      workInProgressHook.next = hook;
    }
    // 记录我们当前已经处理到哪个hook了
    workInProgressHook = hook;
  } else {
    // 如果不是首次渲染，是更新
    hook = workInProgressHook;
    // 记录当前处理的hook
    workInProgressHook = workInProgressHook.next;
  }
  // 根据旧状态来计算新的状态
  let baseState = hook.memoizedState;
  // 如果pending存在
  if (hook.queue.pending) {
    let firstUpdate = hook.queue.pending.next;
    // 因为我们对调用多次 但是只会执行一次，所以我们根据我们的链表队列 循环执行返回最后的值
    do {
      const action = firstUpdate.action;
      // 计算值后 把数据保存到baseState
      baseState = action(baseState);
      firstUpdate = firstUpdate.next;

      // 因为 queue是一个环 如果是判断 firstUpdate 会导致死循环，所以我们要判断 当前的是否非等于第一个
      // 在环形链表下 hook.queue.pending.next 永远指向第一项 也就是最后一个数据
      // 如果等于第一个 表示当前循环的 和第一个一样 也就是到达了最后
    } while (firstUpdate !== hook.queue.pending.next);

    console.log("hook ==> ", hook);

    // 计算完成以后 制空hook的指向
    hook.queue.pending = null;
  }
  // 返回新的数据  hook.memoizedState 就是用来存储新的值的
  hook.memoizedState = baseState;
  // console.log('baseState ====>  ', baseState)
  
  return [baseState, dispatchAction.bind(null, hook.queue)];
}

function dispatchAction(queue, action) {
  // update 要注意的是
  // 当我们出现这种情况
  // setX(1)  setX(2)  setX(3)  setX(4)  setX(5)  setX(6)
  // 我们连续的调用了6次，为了保证她能够按照顺序执行，update的其实也是一个队列，执行完上一个后，执行下一个next直到最后
  const update = {
    action,
    next: null,
  };

  // ueue.pending === null 表示当前的setX(1)没有要更新的状态 执行下一个 setX(1)
  // 因为都是setX本身 所以 action是相同的 所以我们 update.next = update 就可以指向下一个了
  // 因为 队列其实是一个环  最后一个就是头尾相连
  if (queue.pending === null) {
    // u0 -> u0
    update.next = update;
  } else {
    // u1 -> u0 -> u1
    update.next = queue.pending.next;
    queue.pending.next = update;
  }
  // 当调用 setX 时, 改变queue的pending的状态
  queue.pending = update;
  // 数据改变后 重新渲染当前的组件
  schedule();
}

function App() {
  const [num, updateNum] = useState(0);
  const [num1, updateNum1] = useState(1);
  const [num2, updateNum2] = useState(2);
  const [num3, updateNum3] = useState(3);
  const [num4, updateNum4] = useState(4);

  console.log("isMount ===> ", isMount);
  console.log("num ===> ", num);

  return {
    onClick() {
      updateNum((num) => num + 1);
      updateNum((num) => num + 1);
      updateNum((num) => num + 1);
      updateNum((num) => num + 1);
      updateNum((num) => num + 1);
      updateNum((num) => num + 1);
    },
  };
}

window.app = schedule();
